package com.xiaobai.nacosgateway;


import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.StringUtils;
import com.xiaobai.nacosgateway.config.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Configuration
public class CorsConfig {
    private static final String MAX_AGE = "18000L";

    @Resource
    private UserServiceHolder userServiceHolder;


    @Bean
    public WebFilter corsFilter() {
        return (ServerWebExchange ctx, WebFilterChain chain) -> {
            ServerHttpRequest serverHttpRequest = ctx.getRequest();
            ServerHttpResponse serverHttpResponse = ctx.getResponse();
            if (CorsUtils.isCorsRequest(serverHttpRequest)) {
                HttpHeaders requestHeaders = serverHttpRequest.getHeaders();
                HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
                HttpHeaders headers = serverHttpResponse.getHeaders();
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
                headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders
                        .getAccessControlRequestHeaders());
                if(requestMethod != null){
                    headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
                }
                headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
                headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
                headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
                if (serverHttpRequest.getMethod() == HttpMethod.OPTIONS) {
                    serverHttpResponse.setStatusCode(HttpStatus.OK);
                    return Mono.empty();
                }
            }
            String path = serverHttpRequest.getPath().value();
            if (!path.contains("/spike-provider/user")
                    && !path.contains("/spike-provider/oauth2")
                    && !path.contains("/client-app")) {

                String authorization = serverHttpRequest.getHeaders().getFirst("Authorization");

                if (StringUtils.isBlank(authorization)) {
                    serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return getVoidMono(serverHttpResponse, 401, "Authorization 不为空");
                }
                authorization = authorization.replace("bear ", "");
                Future<ApiResponse<User>> token = userServiceHolder.getToken(authorization);


                User user = null;
                try {
                    ApiResponse<User> apiResponse = token.get();
                    if (apiResponse.getCode() != 0) {
                        if (apiResponse.getCode() == 403) {
                            serverHttpResponse.setStatusCode(HttpStatus.FORBIDDEN);
                        }
                        return getVoidMono(serverHttpResponse, apiResponse.getCode(), apiResponse.getMsg());
                    }
                    user = apiResponse.getData();
                } catch (InterruptedException | ExecutionException e) {
                    serverHttpResponse.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    return getVoidMono(serverHttpResponse, 500, "依赖服务异常");
                }

                if (user == null) {
                    serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return getVoidMono(serverHttpResponse, 401, "鉴权失败");
                }
                ServerHttpRequest newRequest = ctx.getRequest().mutate()
                        .header("x-auth-uid", String.valueOf(user.getId()))
                        .build();
                return chain.filter(ctx.mutate().request(newRequest).build());
            }
            return chain.filter(ctx);
        };
    }

    private Mono<Void> getVoidMono(ServerHttpResponse serverHttpResponse, Integer code, String msg) {
        serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        ApiResponse responseResult = new ApiResponse();
        responseResult.setCode(code);
        responseResult.setMsg(msg);
        DataBuffer dataBuffer = serverHttpResponse.bufferFactory().wrap(JSON.toJSONString(responseResult).getBytes());
        return serverHttpResponse.writeWith(Flux.just(dataBuffer));
    }
}
